****************************************************************************
*/
-/*
- * Note from KAF: We should probably be more careful about overflow
- * of our timestamp cycle counter value, as we only use 31 bits of
- * precision. This will overflow on a 3GHz processor in less than a second,
- * and the situation is only going to get worse.
- *
- * Probably we should use bits N-N+31 of the TSC rather than 0-31, and
- * adjust scale_f and scale_i accordingly. If we're really smart we'd
- * calculate N dynamically, according to the measured CPU speed!
- *
- * I think the current code limps along okay for now though.
- */
-
/*
* linux/arch/i386/kernel/time.c
*
unsigned long cpu_khz; /* Detected as we calibrate the TSC */
unsigned long ticks_per_usec; /* TSC ticks per microsecond. */
+/* We use this to prevent overflow of 31-bit RDTSC "diffs". */
+static unsigned int rdtsc_bitshift;
+
spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
int timer_ack=0;
static inline s_time_t __get_s_time(void)
{
s32 delta_tsc;
- u32 low, pcc;
- u64 delta;
- s_time_t now;
-
- pcc = stime_pcc;
- now = stime_now;
-
- /*
- * We only use the bottom 32 bits of the TSC. This should be sufficient,
- * although we take care that TSC on this CPU may be lagging the master TSC
- * slightly. In this case we clamp the TSC difference to a minimum of zero.
- */
- rdtscl(low);
- delta_tsc = low - pcc;
+ u32 low;
+ u64 delta, tsc;
+
+ rdtscll(tsc);
+ low = (u32)(tsc >> rdtsc_bitshift);
+ delta_tsc = (s32)(low - stime_pcc);
if ( unlikely(delta_tsc < 0) ) delta_tsc = 0;
delta = ((u64)delta_tsc * st_scale_f);
delta >>= 32;
delta += ((u64)delta_tsc * st_scale_i);
- return now + delta;
+ return stime_now + delta;
}
s_time_t get_s_time(void)
unsigned long flags;
spin_lock_irqsave(&stime_lock, flags);
- si->system_time = stime_now;
- si->st_timestamp = stime_pcc;
- si->tv_sec = wall_clock_time.tv_sec;
- si->tv_usec = wall_clock_time.tv_usec;
- si->wc_timestamp = wctime_st;
+ si->cpu_freq = cpu_freq;
+ si->rdtsc_bitshift = rdtsc_bitshift;
+ si->system_time = stime_now;
+ si->st_timestamp = stime_pcc;
+ si->tv_sec = wall_clock_time.tv_sec;
+ si->tv_usec = wall_clock_time.tv_usec;
+ si->wc_timestamp = wctime_st;
si->wc_version++;
spin_unlock_irqrestore(&stime_lock, flags);
cpu_freq = cpu_freqs[freq_index];
/* adjust scaling factor */
- scale = 1000000000LL << 32;
+ scale = 1000000000LL << (32 + rdtsc_bitshift);
scale /= cpu_freq;
st_scale_f = scale & 0xffffffff;
st_scale_i = scale >> 32;
unsigned long flags;
s_time_t new_st;
unsigned long usec;
- static int calls_since_scale_update = 0;
+ u64 full_pcc;
+ static int calls_since_scale_update = 0;
spin_lock_irqsave(&stime_lock, flags);
/* Update system time. */
stime_now = new_st = __get_s_time();
- rdtscl(stime_pcc);
-
- /* Maybe update our rate to be in sync with the RTC. */
- if ( ++calls_since_scale_update >=
- (SCALE_UPDATE_PERIOD/TIME_UPDATE_PERIOD) )
- {
- update_scale();
- calls_since_scale_update = 0;
- }
+ rdtscll(full_pcc);
+ stime_pcc = (u32)(full_pcc >> rdtsc_bitshift);
/* Update wall clock time. */
usec = ((unsigned long)(new_st - wctime_st))/1000;
wall_clock_time.tv_usec = usec;
wctime_st = new_st;
+ /* Maybe update our rate to be in sync with the RTC. */
+ if ( ++calls_since_scale_update >=
+ (SCALE_UPDATE_PERIOD/TIME_UPDATE_PERIOD) )
+ {
+ update_scale();
+ calls_since_scale_update = 0;
+ }
+
spin_unlock_irqrestore(&stime_lock, flags);
TRC(printk("TIME[%02d] update time: stime_now=%lld now=%lld,wct=%ld:%ld\n",
u32 cpu_cycle; /* time of one cpu cyle in pico-seconds */
u64 scale; /* scale factor */
s64 freq_off;
+ u64 full_pcc;
+ unsigned int cpu_ghz;
spin_lock_init(&stime_lock);
- printk("Init Time[%02d]:\n", cpu);
+ cpu_ghz = (unsigned int)(cpu_freq / 1000000000ULL);
+ for ( rdtsc_bitshift = 0; cpu_ghz != 0; rdtsc_bitshift++, cpu_ghz >>= 1 )
+ continue;
+
+ printk("Init Time[%02d]: %u\n", cpu, rdtsc_bitshift);
/* System Time */
cpu_cycle = (u32) (1000000000LL/cpu_khz); /* in pico seconds */
- scale = 1000000000LL << 32;
- scale /= cpu_freq;
- st_scale_f = scale & 0xffffffff;
- st_scale_i = scale >> 32;
-
/* calculate adjusted frequencies */
freq_off = cpu_freq/1000; /* .1% */
cpu_freqs[0] = cpu_freq + freq_off;
cpu_freqs[1] = cpu_freq;
cpu_freqs[2] = cpu_freq - freq_off;
+ scale = 1000000000LL << (32 + rdtsc_bitshift);
+ scale /= cpu_freq;
+ st_scale_f = scale & 0xffffffff;
+ st_scale_i = scale >> 32;
+
/* Wall Clock time */
wall_clock_time.tv_sec = get_cmos_time();
wall_clock_time.tv_usec = 0;
/* set starting times */
stime_now = (s_time_t)0;
- rdtscl(stime_pcc);
+ rdtscll(full_pcc);
+ stime_pcc = (u32)(full_pcc >> rdtsc_bitshift);
wctime_st = NOW();
/* start timer to update time periodically */
/* set up the shared info structure */
update_dom_time(p->shared_info);
- p->shared_info->cpu_freq = cpu_freq;
p->shared_info->domain_time = 0;
/* we pass start info struct to guest os as function parameter on stack */
/* Set up shared info area. */
update_dom_time(p->shared_info);
- p->shared_info->cpu_freq = cpu_freq;
p->shared_info->domain_time = 0;
virt_startinfo_address = (start_info_t *)
/*
* Time: The following abstractions are exposed: System Time, Clock Time,
* Domain Virtual Time. Domains can access Cycle counter time directly.
- * XXX RN: Need something to pass NTP scaling to GuestOS.
+ *
+ * The following values are updated periodically (and atomically, from the
+ * p.o.v. of the guest OS). Th eguest OS detects this because the wc_version
+ * is incremented.
*/
-
- u64 cpu_freq; /* to calculate ticks -> real time */
-
+ u32 wc_version; /* a version number for info below */
+ unsigned int rdtsc_bitshift; /* use bits N:N+31 of TSC */
+ u64 cpu_freq; /* to calculate ticks -> real time */
/* System Time */
- long long system_time; /* in ns */
- unsigned long st_timestamp; /* cyclecounter at last update */
-
+ long long system_time; /* in ns */
+ unsigned long st_timestamp; /* cyclecounter at last update */
/* Wall Clock Time */
- u32 wc_version; /* a version number for info below */
- long tv_sec; /* essentially a struct timeval */
+ long tv_sec; /* essentially a struct timeval */
long tv_usec;
- long long wc_timestamp; /* system time at last update */
+ long long wc_timestamp; /* system time at last update */
/* Domain Virtual Time */
unsigned long long domain_time;
extern rwlock_t xtime_lock;
unsigned long cpu_khz; /* get this from Xen, used elsewhere */
-static spinlock_t hyp_stime_lock = SPIN_LOCK_UNLOCKED;
-static spinlock_t hyp_wctime_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t hyp_time_lock = SPIN_LOCK_UNLOCKED;
+static unsigned int rdtsc_bitshift;
static u32 st_scale_f;
static u32 st_scale_i;
static u32 shadow_st_pcc;
* and use the cycle counter value as the "version" number. Clashes
* should be very rare.
*/
-static inline long long get_s_time(void)
+static inline s64 __get_s_time(void)
{
- unsigned long flags;
- u32 delta_tsc, low, pcc;
- u64 delta;
- s64 now;
-
- spin_lock_irqsave(&hyp_stime_lock, flags);
-
- while ((pcc = HYPERVISOR_shared_info->st_timestamp) != shadow_st_pcc)
- {
- barrier();
- shadow_st_pcc = pcc;
- shadow_st = HYPERVISOR_shared_info->system_time;
- barrier();
- }
-
- now = shadow_st;
- /* only use bottom 32bits of TSC. This should be sufficient */
- rdtscl(low);
- delta_tsc = low - pcc;
+ s32 delta_tsc;
+ u32 low;
+ u64 delta, tsc;
+
+ rdtscll(tsc);
+ low = (u32)(tsc >> rdtsc_bitshift);
+ delta_tsc = (s32)(low - shadow_st_pcc);
+ if ( unlikely(delta_tsc < 0) ) delta_tsc = 0;
delta = ((u64)delta_tsc * st_scale_f);
delta >>= 32;
delta += ((u64)delta_tsc * st_scale_i);
- spin_unlock_irqrestore(&hyp_time_lock, flags);
-
- return now + delta;
-
+ return shadow_st + delta;
}
-#define NOW() ((long long)get_s_time())
/*
* Wallclock time.
unsigned long flags;
long usec, sec;
u32 version;
- u64 now;
+ u64 now, cpu_freq, scale;
- spin_lock_irqsave(&hyp_wctime_lock, flags);
+ spin_lock_irqsave(&hyp_time_lock, flags);
- while ((version = HYPERVISOR_shared_info->wc_version)!= shadow_wc_version)
+ while ( (version = HYPERVISOR_shared_info->wc_version) !=
+ shadow_wc_version )
{
barrier();
+
shadow_wc_version = version;
shadow_tv_sec = HYPERVISOR_shared_info->tv_sec;
shadow_tv_usec = HYPERVISOR_shared_info->tv_usec;
shadow_wc_timestamp = HYPERVISOR_shared_info->wc_timestamp;
+ shadow_st_pcc = HYPERVISOR_shared_info->st_timestamp;
+ shadow_st = HYPERVISOR_shared_info->system_time;
+
+ rdtsc_bitshift = HYPERVISOR_shared_info->rdtsc_bitshift;
+ cpu_freq = HYPERVISOR_shared_info->cpu_freq;
+
+ /* XXX cpu_freq as u32 limits it to 4.29 GHz. Get a better do_div! */
+ scale = 1000000000LL << (32 + rdtsc_bitshift);
+ do_div(scale,(u32)cpu_freq);
+ st_scale_f = scale & 0xffffffff;
+ st_scale_i = scale >> 32;
+
barrier();
}
- now = NOW();
+ now = __get_s_time();
usec = ((unsigned long)(now-shadow_wc_timestamp))/1000;
sec = shadow_tv_sec;
usec += shadow_tv_usec;
struct timeval tv;
long long time, delta;
-#ifdef XENO_TIME_DEBUG
- static u32 foo_count = 0;
- foo_count++;
- if (foo_count>= 1000) {
- s64 n = NOW();
- struct timeval tv;
- do_gettimeofday(&tv);
- printk("0x%08X%08X %ld:%ld\n",
- (u32)(n>>32), (u32)n, tv.tv_sec, tv.tv_usec);
- foo_count = 0;
- }
-#endif
/*
* The next bit really sucks:
* Linux not only uses do_gettimeofday() to keep a notion of
* updates xtime accordingly. Yuck!
*/
- /* work out the number of jiffies past and update them */
+ /* Work out the number of jiffy intervals passed and update them. */
do_gettimeofday(&tv);
time = (((long long)tv.tv_sec) * 1000000) + tv.tv_usec;
delta = time - last_irq;
void __init time_init(void)
{
unsigned long long alarm;
- u64 cpu_freq = HYPERVISOR_shared_info->cpu_freq;
- u64 scale;
+ u64 __cpu_khz;
- cpu_khz = (u32)cpu_freq/1000;
+ __cpu_khz = HYPERVISOR_shared_info->cpu_freq;
+ do_div(__cpu_khz, 1000);
+ cpu_khz = (u32)__cpu_khz;
printk("Xen reported: %lu.%03lu MHz processor.\n",
cpu_khz / 1000, cpu_khz % 1000);
- /*
- * calculate systemtime scaling factor
- * XXX RN: have to cast cpu_freq to u32 limits it to 4.29 GHz.
- * Get a better do_div!
- */
- scale = 1000000000LL << 32;
- do_div(scale,(u32)cpu_freq);
- st_scale_f = scale & 0xffffffff;
- st_scale_i = scale >> 32;
- printk("System Time scale: %X %X\n",st_scale_i, st_scale_f);
-
do_gettimeofday(&xtime);
last_irq = (((long long)xtime.tv_sec) * 1000000) + xtime.tv_usec;